﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
using Volo.Abp.Linq;
using Volo.Abp.Uow;
using YaAppoint.Appointments.ManualAppoints;
using YaAppoint.Appointments.Schedules;
using YaAppoint.Appointments.Schedules.Pools;
using YaAppoint.Common;
using YaAppoint.CustomSettings;
using YaAppoint.Devices;
using YaAppoint.Response;

namespace YaAppoint.Appointments.Pools
{
    /// <summary>
    /// 号池服务
    /// </summary>
    [RemoteService(isEnabled: false)]
    public class PoolAppService(IRepository<Pool,Guid> poolRepository,
                                IMapper mapper,
                                ICommonAppService commonAppService,
                                IGuidGenerator guidGenerator,
                                AsyncQueryableExecuter queryableExecuter,
                                PoolManager poolManager,
                                IRepository<Device,Guid> deviceReposotory,
                                IRepository<PlanPool,Guid> planPoolRepository,
                                ICustomSettingAppService customSettingAppService,
                                IUnitOfWorkManager unitOfWorkManager,
                                IRepository<Schedule,Guid> scheduleRepository,
                                IRepository<ScheduleOverride,Guid> scheduleOverrideRepository) : ApplicationService, IPoolAppService, ITransientDependency
    {
        private readonly IRepository<Pool, Guid> _poolRepository = poolRepository;
        private readonly IMapper _mapper = mapper;
        private readonly IRepository<ScheduleOverride, Guid> _scheduleOverrideRepository = scheduleOverrideRepository;
        private readonly AsyncQueryableExecuter _queryableExecuter= queryableExecuter;
        private readonly PoolManager _poolManager = poolManager;
        private readonly IRepository<Device, Guid> _deviceReposotory= deviceReposotory;
        private readonly IRepository<PlanPool, Guid> _planPoolRepository = planPoolRepository;
        private readonly ICommonAppService _commonAppService= commonAppService;
        private readonly IGuidGenerator _guidGenerator = guidGenerator;
        private readonly ICustomSettingAppService _customSettingAppService = customSettingAppService;
        private readonly IRepository<Schedule, Guid> _scheduleRepository = scheduleRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager = unitOfWorkManager;


        /// <summary>
        /// 提交新建
        /// </summary>
        public async Task<BackJson> SubmitCreate(CreatePoolDto dto)
        {
            dto.StartTime = new DateTime(dto.Date.Year, dto.Date.Month, dto.Date.Day, dto.StartTime.Hour, dto.StartTime.Minute, dto.StartTime.Second);
            dto.EndTime = new DateTime(dto.Date.Year,dto.Date.Month,dto.Date.Day,dto.EndTime.Hour,dto.EndTime.Minute,dto.EndTime.Second);
            var pool = _mapper.Map<CreatePoolDto, Pool>(dto);
            await _poolRepository.InsertAsync(pool);
            return new BackJson()
            {
                code = 0,
                msg = "保存成功"
            };
        }

        /// <summary>
        /// 获取分页列表、
        /// （实际号池）
        /// </summary>
        public async Task<BackJson> GetPoolList(Guid sid, int page, int pageSize)
        {
            var query = await _poolRepository.GetQueryableAsync();
            query = query.Where(x => x.ScheduleOverrideId == sid).OrderBy(x=>x.StartTime);
            var total = await _queryableExecuter.CountAsync(query);
            query = query.Skip(pageSize * (page - 1)).Take(pageSize);
            var list = await _queryableExecuter.ToListAsync(query);

            return new BackJson()
            {
                code = 0,
                data = new
                {
                    total = total,
                    list = _mapper.Map<List<Pool>, List<PoolDto>>(list)
                }
            };
        }

        /// <summary>
        /// 号池获取
        /// 根据 日期 和 设备 获取号池
        /// </summary>
        /// <param name="queryDto"></param>
        /// <returns></returns>
        public async Task<BackJson> GetPoolList(PoolQueryDto queryDto)
        {
            var data = new List<Dictionary<string,object?>>();
            List<Pool>? list = null;
            var date = queryDto.Date.Date;
            var device = await _deviceReposotory.FirstOrDefaultAsync(d => d.Id == queryDto.DeviceId);
            if (device == null)
            {
                throw new BusinessException(CustomErrorCodes.DeviceNotExist);
            }

            var setting = await _customSettingAppService.GetDeviceSetting(device.Id);

            // 试图加载给定日期实际号池
            var query = await _poolRepository.GetQueryableAsync();
            query = query.Where(p => p.DeviceId == queryDto.DeviceId && p.Date.Date == date && p.IsActive).OrderBy(p => p.StartTime);
            // 最晚预约时间大于当前时间
            if (setting.LatestAppoint != null)
            {
                query = query.Where(p => p.EndTime >=DateTime.Now.AddMinutes(10));
            }
           
            list = await _queryableExecuter.ToListAsync(query);
            
            // 给定日期、给定设备有无特殊排班计划
            var scheduleOverride = await _scheduleOverrideRepository.FirstOrDefaultAsync(s => s.Date.Date == date && s.DeviceId == queryDto.DeviceId);
            if (scheduleOverride != null) // 有特殊排班计划
            {
                list = list.Where(e => e.Type == PoolType.SpecialPool).ToList(); // 当天的特殊号池
                // 实际号池都属于特殊排班
                foreach (var realPool in list)
                {
                    var pool = new Dictionary<string, object?>
                    {
                        { "id", realPool.Id },
                        { "name", realPool.Name },
                        { "current", realPool.Current },
                        { "startTime", realPool.StartTime.ToString("HH:mm") },
                        { "endTime", realPool.EndTime.ToString("HH:mm") },
                        { "maxCount", realPool.MaxCount },
                        { "planPoolId", null },
                        { "allowOverFlow", realPool.AllowOverFlow }
                    };
                    data.Add(pool);
                }
            }
            else // 普通排班
            {
                if (device.ScheduleId == null)
                {
                    throw new BusinessException(CustomErrorCodes.DeviceHasNoScheduleOrSO)
                    {
                        Data = {
                            { "deviceName",device.Name}
                        }
                    };
                }
                else // 按排班计划走，无特殊排班
                {
                    list = list.Where(e => e.Type == PoolType.Pool).ToList();
                    var dayInWeek = _commonAppService.ToDayInWeek(date.DayOfWeek);
                    var planPools = device.Schedule!.PlanPools.Where(pp => pp.DayInWeek == dayInWeek && pp.IsActive)
                                                 .Where(pp=>date.Add(pp.TimeInterval.EndTime)>=DateTime.Now.AddMinutes(10))
                                            .OrderBy(pp => pp.TimeInterval.StartTime).ToList();

                    foreach (var item in planPools)
                    {
                        var realPool = list.FirstOrDefault(p => p.PlanPoolId == item.Id);
                        var pool = new Dictionary<string, object?>();
                        if (realPool != null) // 存在对应实际号池
                        {
                            pool.Add("id", realPool.Id);
                            pool.Add("name", realPool.Name);
                            pool.Add("current", realPool.Current);
                        }
                        else  // 虚拟号池
                        {
                            pool.Add("id", null);
                            pool.Add("name", null);
                            pool.Add("current", 0);
                        }
                        pool.Add("key", _guidGenerator.Create().ToString());
                        pool.Add("startTime", item.TimeInterval.StartTime.ToString(@"hh\:mm"));
                        pool.Add("endTime", item.TimeInterval.EndTime.ToString(@"hh\:mm"));
                        pool.Add("maxCount", item.MaxCount);
                        pool.Add("planPoolId", item.Id);
                        pool.Add("allowOverFlow", item.AllowOverFlow);
                        data.Add(pool);
                    }
                }
            }

            return new BackJson()
            {
                code = 0,
                data = data
            };
        }


        /// <summary>
        /// 提交编辑
        /// </summary>
        public async Task<BackJson> SubmitEdit(PoolDto dto)
        {
            var startTime =TimeSpan.Parse(dto.StartTimeStr);
            var endTime =TimeSpan.Parse( dto.EndTimeStr);
            var date = dto.Date;
            var start = new DateTime(date.Year, date.Month, date.Day, startTime.Hours, startTime.Minutes, startTime.Seconds);
            var end = new DateTime(date.Year, date.Month, date.Day, endTime.Hours, endTime.Minutes, endTime.Seconds);

            var pool = await _poolRepository.FirstOrDefaultAsync(x => x.Id == dto.Id);
            if (pool == null)
            {
                throw new BusinessException(CustomErrorCodes.PoolNotExist);
            }
            if (pool.Current > 0)
            {
                throw new BusinessException(CustomErrorCodes.AppointmentAlreadyInPool);
            }

            pool.StartTime = start;
            pool.EndTime = end;
            pool.MaxCount = dto.MaxCount;
            pool.AllowOverFlow = dto.AllowOverFlow;
            pool.IsActive = dto.IsActive;

            await _poolRepository.UpdateAsync(pool);
            return new BackJson()
            {
                code=0,
                msg="保存成功"
            };
        }

        /// <summary>
        /// 获取详情
        /// </summary>
        public async Task<BackJson> GetAsync(Guid id)
        {
            var pool = await _poolRepository.FirstOrDefaultAsync(x => x.Id == id);
            if (pool == null)
            {
                throw new BusinessException(CustomErrorCodes.PoolNotExist);
            }
            return new BackJson()
            {
                code = 0,
                data = _mapper.Map<Pool, PoolDto>(pool)
            };
        }

        /// <summary>
        /// 删除
        /// </summary>
        public async Task<BackJson> DeletePool(Guid id)
        {
            await  _poolManager.DeleteAsync(id);

            return new BackJson()
            {
                code = 0,
                msg = "删除成功"
            };
        }

        /// <summary>
        /// 批量删除
        /// </summary>
        public async Task<BackJson> DeletePoolMany(List<Guid> idList)
        {
            foreach (var id in idList)
            {
                await _poolManager.DeleteAsync(id);
            }
            return new BackJson()
            {
                code = 0,
                msg = "删除成功"
            };
        }


        /// <summary>
        /// 批量创建特殊排班号池
        /// </summary>
        public async Task<BackJson> BatchCreate(BatchCreatePoolModel batchModel, Guid sid)
        {
            var schduleOverride = await _scheduleOverrideRepository.FirstOrDefaultAsync(x => x.Id == sid);
            if (schduleOverride == null)
            {
                throw new BusinessException(CustomErrorCodes.ScheduleOverrideNotExist);
            }
            var date = batchModel.Date;
            var startTime = new DateTime(date.Year, date.Month, date.Day, batchModel.StartTime.Hours, batchModel.StartTime.Minutes, batchModel.StartTime.Seconds);
            var endTime = new DateTime(date.Year,date.Month,date.Day,batchModel.EndTime.Hours,batchModel.EndTime.Minutes,batchModel.EndTime.Seconds);

            for (DateTime start = startTime; start.AddMinutes(batchModel.Interval) <= endTime;start= start.AddMinutes(batchModel.Interval))
            {
                var pool = new Pool()
                {
                    IsActive = true,
                    Device = schduleOverride.Device,
                    DeviceId = schduleOverride.DeviceId,
                    ScheduleOverrideId = sid,
                    StartTime = start,
                    EndTime = start.AddMinutes(batchModel.Interval),
                    MaxCount = batchModel.MaxCount,
                    AllowOverFlow = batchModel.AllowOverFlow,
                    Date = batchModel.Date
                };

                await _poolRepository.InsertAsync(pool);    
            }

            return new BackJson()
            {
                code = 0,
                msg = "批量创建成功"
            };
        }


        /// <summary>
        /// 禁用与启用
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<BackJson> EnableToggle(Guid id)
        {
            var pool = await _poolRepository.FirstOrDefaultAsync(x => x.Id == id);
            if (pool == null)
            {
                throw new BusinessException(CustomErrorCodes.PoolNotExist);
            }
            pool.IsActive = !pool.IsActive;
            await _poolRepository.UpdateAsync(pool);
            return new BackJson()
            {
                code = 0,
                msg = "success"
            };
        }


        /// <summary>
        /// 检测并创建给定设备给定日期的所有号池
        /// 
        /// 根据号池计划来，有号池计划就对应创建
        /// 如果为特殊排班，则不创建
        /// </summary>
        public async Task CreateDeviceAllPoolsOnDate(Guid deviceId, DateTime date)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                var device = await _deviceReposotory.FirstOrDefaultAsync(d => d.Id == deviceId);
                var existingPools = await _poolRepository.GetListAsync(p => p.DeviceId == deviceId && p.Date.Date == date.Date);// 指定设备、指定日期已存在的号池
                if (device == null)
                {
                    throw new BusinessException(CustomErrorCodes.DeviceNotExist);
                }
                // (指定设备、指定日期)是否有特殊排班

                var scheduleOverride = await _scheduleOverrideRepository.FirstOrDefaultAsync(so => so.DeviceId == deviceId && so.Date.Date == date.Date);
                if (scheduleOverride != null)
                {
                    return;
                }

                // (指定设备指定日期无特殊排班)排班计划
                var schedule = await _scheduleRepository.FirstOrDefaultAsync(s => s.DepartmentId == device.DepartmentId && s.DeviceTypeId == device.DeviceTypeId);
                if (schedule == null) // 没有排班计划
                {
                    return;
                }
                var dayInWeek = _commonAppService.ToDayInWeek(date.DayOfWeek);
                var plainPools = schedule.PlanPools.Where(pp => pp.DayInWeek == dayInWeek && pp.IsActive).ToList();
                var plaiPoolIds = plainPools.Select(p => p.Id).ToList();
                if (plainPools.Count == 0) // 无任何计划号池
                {
                    return;
                }
                var plainQuery = await _planPoolRepository.GetQueryableAsync();
                plainQuery = plainQuery.Include(p => p.TimeInterval).Where(p => plaiPoolIds.Contains(p.Id));
                var plainPoolList = await _queryableExecuter.ToListAsync(plainQuery);
                foreach (var plainPool in plainPoolList)
                {
                    var pool = existingPools.FirstOrDefault(p => p.PlanPoolId == plainPool.Id);
                    if (pool == null)
                    {
                        // 根据号池计划创建号池
                        var entity = new Pool();
                        var interval = plainPool.TimeInterval;
                        entity.AllowOverFlow = plainPool.AllowOverFlow;
                        entity.Current = 0;
                        entity.StartTime = new DateTime(date.Date.Year, date.Date.Month, date.Date.Day, interval.StartTime.Hours, interval.StartTime.Minutes, interval.StartTime.Seconds);
                        entity.EndTime = new DateTime(date.Date.Year, date.Date.Month, date.Date.Day, interval.EndTime.Hours, interval.EndTime.Minutes, interval.EndTime.Seconds);
                        entity.Date = date.Date;
                        entity.PlanPoolId = plainPool.Id;
                        entity.ScheduleOverrideId = null;
                        entity.DeviceId = deviceId;
                        entity.MaxCount = plainPool.MaxCount;
                        entity.IsActive = true;

                        await _poolRepository.InsertAsync(entity);
                    }
                }
                await uow.CompleteAsync(); // 结束独立工作单元
            }
            return;
        }

        public async Task CreateAllDevicesAllPools(List<DeviceDto> devices, DateTime date)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                var deviceIds = devices.Select(d => d.Id).ToList();
                // 给定日期下，所有可用设备的所有现存号池
                var allDevicesExistingPools = await _poolRepository.GetListAsync(p => deviceIds.Contains(p.DeviceId) && p.Date.Date == date.Date);
                var allSchduleOverrides = await _scheduleOverrideRepository.GetListAsync(s => s.Date.Date == date.Date && deviceIds.Contains(s.DeviceId));
                var dayInWeek = _commonAppService.ToDayInWeek(date.DayOfWeek);
                var schduleIds = devices.Select(d => d.ScheduleId);
                var query = await _planPoolRepository.GetQueryableAsync();
                query = query.Include(p => p.TimeInterval).Where(p => schduleIds.Contains(p.ScheduleId) && p.DayInWeek == dayInWeek);
                var allPlanPools = await _queryableExecuter.ToListAsync(query);

                List<Pool> poolsToAdd = new List<Pool>();
                foreach (var device in devices)
                {
                    // 是否有特殊号池
                    var scheduleOverride = allSchduleOverrides.FirstOrDefault(so => so.DeleterId == device.Id && so.Date.Date == date.Date);
                    if (scheduleOverride != null)
                    {
                        continue;
                    }

                    // 当前设备在指定日期下是否有排班计划
                    if (device.ScheduleId == null)
                    {
                        continue;
                    }

                    var planPools = allPlanPools.Where(p => p.ScheduleId == device.ScheduleId && p.DayInWeek == dayInWeek).ToList();
                    foreach (var plainPool in planPools)
                    {
                        var pool = allDevicesExistingPools.FirstOrDefault(p => p.PlanPoolId == plainPool.Id);
                        if (pool == null)
                        {
                            pool = new Pool();
                            var interval = plainPool.TimeInterval;
                            pool.AllowOverFlow = plainPool.AllowOverFlow;
                            pool.Current = 0;
                            pool.StartTime = new DateTime(date.Date.Year, date.Date.Month, date.Date.Day, interval.StartTime.Hours, interval.StartTime.Minutes, interval.StartTime.Seconds);
                            pool.EndTime = new DateTime(date.Date.Year, date.Date.Month, date.Date.Day, interval.EndTime.Hours, interval.EndTime.Minutes, interval.EndTime.Seconds);
                            pool.Date = date.Date;
                            pool.PlanPoolId = plainPool.Id;
                            pool.ScheduleOverrideId = null;
                            pool.DeviceId = device.Id!.Value;
                            pool.MaxCount = plainPool.MaxCount;
                            pool.IsActive = true;
                            poolsToAdd.Add(pool);
                        }
                    }
                }
                await _poolRepository.InsertManyAsync(poolsToAdd);
                await uow.CompleteAsync();
            }
        }

    }
}
